/*
 *  Copyright (C) 2009  Peter Kist & Jan Ripke
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <string>

#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;

#include <boost/thread/thread.hpp>
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <boost/date_time/posix_time/posix_time_types.hpp>

#include <galaxy/AppContext.h>
#include <Ogre.h>
#include <OgreLogManager.h>

#ifdef TEST_CEGUI
#include <CEGUI/CEGUI.h>
#include <OgreCEGUIRenderer.h>
#endif // TEST_CEGUI

#include <fstream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>


#include <core/Timer.h>

#include <gfx/ogre/Manager.h>
#include <galaxy/ModuleManager.h>

#include <boost/foreach.hpp>

using namespace std;
using namespace galaxy;

#if _WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

class TestClass : public boost::enable_shared_from_this<TestClass>, public galaxy::ModuleManager {
public:
    TestClass () { };
    TestClass (int _i, int _j) { i = _i; j = _j; };
    virtual ~TestClass () { i = j = -1; };

    virtual bool initialize () { int t = i; i = j; j = t; return true;};

    virtual bool func () { return shared_from_this(); };

    //virtual bool initialize () = 0;
    virtual bool postInitialize () { return true; };
    virtual boost::thread * startManagerThread () { return 0L;};
    virtual void          terminateThread () { };
    virtual bool run () { return true; };
    virtual bool preDestroy () { return true; };
    virtual bool destroy () { return true; };

    int i, j;
};

class TestManager {
public:
    /** shorthand for a shared pointer to a manager */
    typedef boost::shared_ptr<TestClass> TestPtr;
    /** shorthand for the manager map type */
    typedef boost::unordered_map<int, TestPtr> TestMap;

    /** manager hash */
    TestMap mManagers;

    /** singleton manager factory */
    static TestManager * mSingleton; 

    static TestManager& singleton () { return *mSingleton; };


    static TestPtr TestManager::getManager(int category) {
        TestManager& factory = TestManager::singleton ();
        TestPtr manager = factory.mManagers[(int)category];
        if (! manager) {
            switch (category) {
                case 1:
                    manager.reset(new TestClass(1,2));
                    break;
                case 2:
                    manager.reset(new TestClass(3,4));
                    break;
            }
        }

        factory.mManagers[(int)category] = manager;

        return manager;
    }


    static TestMap TestManager::getTestMap () {
        TestManager& factory = singleton();
        return factory.mManagers;
    }
};


#ifdef TEST_CEGUI
void testCEGUI () {
    Ogre::Root * m_Root = new Ogre::Root("config/plugins.cfg",
        "ogre.cfg",
        "logs/ogre.log");

    //Ogre::LogManager * lmngr = LogManager::getSingletonPtr();
    //lmngr->setLogDetail(Ogre::LL_BOREME);

    Ogre::String secName, typeName, archName;
    Ogre::ConfigFile cf;
    cf.load("config/resources.cfg");

    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
        Ogre::ConfigFile::SettingsMultiMap::iterator i;

        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName,
                typeName, secName);
        }
    }

    if (!m_Root->restoreConfig() && !m_Root->showConfigDialog()) {
        throw Ogre::Exception(52, "User canceled the config dialog!",
            "gfx::Manager::setupRenderSystem()");
    }

    // initialize render window
    m_Root->initialise(true, "MP-Galaxy");

    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

    Ogre::SceneManager *sceneManager = m_Root->createSceneManager(Ogre::ST_GENERIC,
        "Default SceneManager");
    Ogre::Camera *camera = sceneManager->createCamera("Camera");
    Ogre::Viewport *vp = m_Root->getAutoCreatedWindow()->addViewport(camera);



    Ogre::SceneManager *mgr = m_Root->getSceneManager("Default SceneManager");
    Ogre::RenderWindow *win = m_Root->getAutoCreatedWindow();

    // CEGUI setup
    CEGUI::System *mSystem;
    CEGUI::Renderer *mRenderer;

    try {
        mRenderer = new CEGUI::OgreCEGUIRenderer(win, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mgr);
        mSystem = new CEGUI::System(mRenderer);

        // set the default resource groups to be used
        //CEGUI::Imageset::setDefaultResourceGroup("imagesets");
        //CEGUI::Font::setDefaultResourceGroup("fonts");
        //CEGUI::Scheme::setDefaultResourceGroup("schemes");
        //CEGUI::WidgetLookManager::setDefaultResourceGroup("looknfeels");
        //CEGUI::WindowManager::setDefaultResourceGroup("layouts");

        CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Insane);
        CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");
        mSystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");

        CEGUI::FontManager::getSingleton().createFont("commonwealth.font");
        mSystem->setDefaultFont((CEGUI::utf8*)"Commonwealth-10");

        // set the mouse cursor initially in the middle of the screen
        mSystem->injectMousePosition((float)win->getWidth() / 2.0f, (float)win->getHeight() / 2.0f);

        // load a layout from the XML layout file (you'll find this in resources/gui.zip), and 
        // put it in the GUI resource group
        CEGUI::Window* pLayout = CEGUI::WindowManager::getSingleton().loadWindowLayout("test.layout", "", "GUI");

        // you need to tell CEGUI which layout to display. You can call this at any time to change the layout to
        // another loaded layout (i.e. moving from screen to screen or to load your HUD layout). Note that this takes
        // a CEGUI::Window instance -- you can use anything (any widget) that serves as a root window.
        mSystem->setGUISheet(pLayout);

    } catch (Ogre::FileNotFoundException& e) {
        Ogre::String p = e.getDescription();
    } catch (Ogre::Exception& e) {
        Ogre::String p2 = e.getDescription();
    } catch (std::exception& e) {
        const char * p3 = e.what();
    }

    m_Root->startRendering();
}
#endif // TEST_CEGUI

class KeyDescr {
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & BOOST_SERIALIZATION_NVP(m_Key)
            & BOOST_SERIALIZATION_NVP(m_Shift) 
            & BOOST_SERIALIZATION_NVP(m_Alt)
            & BOOST_SERIALIZATION_NVP(m_Ctrl)
            & BOOST_SERIALIZATION_NVP(m_Action);
    }

    int m_Key;
    bool m_Shift;
    bool m_Alt;
    bool m_Ctrl;
    int m_Action;
public:
    KeyDescr (int key, bool shift, bool alt, bool ctrl, int action) 
        : m_Key(key), m_Shift(shift), m_Alt(alt), m_Ctrl(ctrl), m_Action(action)
    {
    }
};

void testSerialization () {
    std::ofstream ofs("test.keymap");
    const KeyDescr bKey('b', false, false, false, 3);
    const KeyDescr cKey('c', true, true, true, 8);
    boost::archive::xml_oarchive oa(ofs);
    oa << BOOST_SERIALIZATION_NVP(bKey);
    oa << BOOST_SERIALIZATION_NVP(cKey);
}


using namespace boost::posix_time;

void testTimer () {
    core::Timer timer;

    // assume that we want to iterate through this loop once every 100 ms
    for (int i = 0; i < 10; i++) {
        timer.updateTimer();

        // do something (or at least pretend ;)
        boost::this_thread::sleep(boost::posix_time::milliseconds(4));
        
        time_duration elapsed = timer.getElapsedDuration ();
        if (elapsed < milliseconds(100)) {
            time_duration td = milliseconds(100) - elapsed;
            boost::this_thread::sleep(td);
        }
    }

    /*
    int ticksPerSec = time_duration::ticks_per_second();
    int ticksPerLoop = ticksPerSec / 100; // should be 10 ms
    ptime timer(microsec_clock::local_time());

    for (int i = 0; i < 10; i++) {
        boost::this_thread::sleep(boost::posix_time::milliseconds(4));

        ptime currentTime(microsec_clock::local_time());
        time_duration elapsed = currentTime - timer;

        int elapsedTicks = elapsed.ticks ();
        if (elapsedTicks < ticksPerLoop) {
            boost::this_thread::sleep(boost::posix_time::milliseconds(ticksPerLoop - elapsedTicks));
        }
        timer = currentTime;
    }
    */
}


TestManager * TestManager::mSingleton = new TestManager ();

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
    TestManager::getManager(1);
    TestManager::getManager(2);

    TestManager::TestMap managers = TestManager::getTestMap();
    /*TestManager::TestMap::iterator iter = managers.begin();
    for (TestManager::TestMap::iterator i = managers.begin();
    i != managers.end (); i++) {
    */
    BOOST_FOREACH (TestManager::TestMap::value_type i, managers) {
        i.second->initialize();
    }

    BOOST_FOREACH (TestManager::TestMap::value_type i, managers) {
        i.second->func();
    }

#ifdef TEST_CEGUI
    testCEGUI();
#endif

    testSerialization();

    testTimer();

    return 0;
}